#include <stddef.h>
#include <stdbool.h>
#include "string.h"
#include "ocall.h"
#include "print.h"

extern unsigned long EAPP_OCALL(unsigned long ocall_func_id);

int vsnprintf(char* out, size_t n, const char* s, va_list vl)
{
    bool format = false;
    bool longarg = false;
    size_t pos = 0;
    for( ; *s; s++)
    {
        if(format)
        {
            switch(*s)
            {
                case 'l':
                    longarg = true;
                    break;
                case 'p':
                    longarg = true;
                    if (++pos < n) out[pos-1] = '0';
                    if (++pos < n) out[pos-1] = 'x';
                case 'x':
                {
                    long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
                    for(int i = 2*(longarg ? sizeof(long) : sizeof(int))-1; i >= 0; i--) {
                        int d = (num >> (4*i)) & 0xF;
                        if (++pos < n) out[pos-1] = (d < 10 ? '0'+d : 'a'+d-10);
                    }
                    longarg = false;
                    format = false;
                    break;
                }
                case 'd':
                {
                    long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
                    if (num < 0) {
                        num = -num;
                        if (++pos < n) out[pos-1] = '-';
                    }
                    long digits = 1;
                    for (long nn = num; nn /= 10; digits++)
                        ;
                    for (int i = digits-1; i >= 0; i--) {
                        if (pos + i + 1 < n) out[pos + i] = '0' + (num % 10);
                        num /= 10;
                    }
                    pos += digits;
                    longarg = false;
                    format = false;
                    break;
                }
                case 's':
                {
                    const char* s2 = va_arg(vl, const char*);
                    while (*s2) {
                        if (++pos < n)
                            out[pos-1] = *s2;
                        s2++;
                    }
                    longarg = false;
                    format = false;
                    break;
                }
                case 'c':
                {
                    if (++pos < n) out[pos-1] = (char)va_arg(vl,int);
                    longarg = false;
                    format = false;
                    break;
                }
                default:
                    break;
            }
        }
        else if(*s == '%')
            format = true;
        else
        if (++pos < n) out[pos-1] = *s;
    }
    if (pos < n)
        out[pos] = 0;
    else if (n)
        out[n-1] = 0;
    return pos;
}

void vprintf(const char*s, va_list vl)
{
    vsnprintf((void*)ENCLAVE_DEFAULT_KBUFFER, 512, s, vl);
    EAPP_OCALL(OCALL_SYS_WRITE);
}

void eapp_print(const char*s, ...)
{
    va_list vl;

    va_start(vl, s);
    vprintf(s, vl);
    va_end(vl);
}